/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package es.unavarra.mti.iws;

import es.unavarra.mti.iws.engine.Engine;
import es.unavarra.mti.iws.gui.MainFrame;
import es.unavarra.mti.iws.gui.AboutDialog;
import es.unavarra.mti.iws.gui.AdvancedQueryDialog;
import es.unavarra.mti.iws.gui.RenderPanel;
import es.unavarra.mti.iws.logic.EngineEvaluator;
import es.unavarra.mti.iws.logic.LogicRunner;
import es.unavarra.mti.iws.logic.editor.KeyboardEvent;
import es.unavarra.mti.iws.logic.editor.TouchEvent;
import es.unavarra.mti.iws.logic.undoredo.UndoManager;
import es.unavarra.mti.iws.model.Model;
import es.unavarra.mti.iws.model.Node;
import es.unavarra.mti.iws.utils.DialogUtils;
import es.unavarra.mti.iws.utils.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.filechooser.FileFilter;

import javax.swing.JOptionPane;
import java.util.Iterator;
import java.util.List;

/**
 *
 * @author Asier
 */
public class Manager {
    private final MainFrame frame;
    private final AdvancedQueryDialog queryDialog;
    private final EngineInterface engine;
    private final LogicRunner runner;
    private final EngineEvaluator evaluator;
    private final UndoManager undo;
    private final RenderPanel panel;
    private Model model;
    private final ConcurrentLinkedQueue<TouchEvent> touchEvents;
    private final ConcurrentLinkedQueue<KeyboardEvent> keyboardEvents;
    private String lastFilePath;
    private FileFilter filter;


    public Manager() {
        touchEvents = new ConcurrentLinkedQueue<TouchEvent>();
        keyboardEvents = new ConcurrentLinkedQueue<KeyboardEvent>();
        panel = new RenderPanel(this);
        frame = new MainFrame(this, panel);
        queryDialog = new AdvancedQueryDialog(this, frame);
        engine = new Engine(); //TODO replace with the real one
        runner = new LogicRunner(this);
        model = new Model();
        model.setManager(this);
        evaluator = new EngineEvaluator(this);
        undo = new UndoManager(64, this);
        filter = new FileFilter() {

            public boolean accept(File file) {
                String filename = file.getName().toLowerCase();
                return (filename.endsWith(".sss")
                        || file.isDirectory());
            }

            public String getDescription() {
                return "Modelo sss";
            }
        };
    }

    void start() {
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        runner.start();
        runner.play();
        evaluator.start();
        MainFrame.getInstance().log("Iniciado, pulse C para limpiar el log");
        engine.init();
    }

    public void startSimulation(){
        runner.play();
        evaluator.play();
    }

    public void stopSimulation(){
        evaluator.pause();
        synchronized(model){
            synchronized(model.getNodes()){
                for(Node n : model.getNodes()){
                    n.setHasFire(false);
                    n.setHasQuake(false);
                    n.setHasRain(false);
                }
            }
        }
    }

    public EngineInterface getEngine() {
        return engine;
    }

    public MainFrame getFrame() {
        return frame;
    }

    public LogicRunner getRunner() {
        return runner;
    }

    public UndoManager getUndo() {
        return undo;
    }

    public RenderPanel getPanel() {
        return panel;
    }

    public Model getModel() {
        return model;
    }

    public void setModel(Model model) {
        this.model = model;
    }

    
    public void addTouchEvent(TouchEvent touchEvent) {
        touchEvents.add(touchEvent);
    }

    public void addKeyEvent(KeyboardEvent keyboardEvent) {
        keyboardEvents.add(keyboardEvent);
    }

    public TouchEvent getTouchEvent(){
        return touchEvents.poll();
    }

    public KeyboardEvent getKeyEvent(){
        return keyboardEvents.poll();
    }

    public void newModel(){
        if(!getFrame().isSaved()){
            int result = DialogUtils.getBooleanDialog(getFrame(), "¿Desea guardar antes?");
            if (result == 1){
                saveModel();
            }else if (result == 2){
                return;
            }else{
            }
        }

        Model oldModel = model;
        synchronized(oldModel){
            createNewModel();
        }
    }

    private void createNewModel() {
        model = new Model();
        model.setManager(this);
        engine.init();
        getUndo().clear();
        getFrame().setFileName("");
        lastFilePath = null;
        getFrame().setSaved(true);
    }

    public void loadModel(){
        String path = FileUtils.selectFile(getFrame(), "cargar", filter, null);
        if(path != null){
            Model oldModel = model;
            synchronized(oldModel){
                File f = new File(path);
                try {
                    
                    Model newModel = (Model) FileUtils.xmlToObject(FileUtils.getBytesFromFile(f));
                    synchronized(newModel){
                        model = newModel;
                        model.setManager(this);
                        engine.init();
                        getUndo().clear();
                        getFrame().setFileName(f.getName());
                        lastFilePath = path;
                        getFrame().setSaved(true);
                        model.reAddItems();
                    }
                } catch (IOException ex) {
                    ex.printStackTrace();
                    MainFrame.getInstance().error("Cargando " + ex.toString());
                }

            }
        }
    }

    public void saveModel() {
        if (lastFilePath == null){
            String path = FileUtils.selectNonExistingFile(getFrame(), ".sss");
            if(path == null) { return; }
            lastFilePath = path;
        }
        saveModelIn(lastFilePath);
    }

    public void saveModelAs(){
        String path = FileUtils.selectNonExistingFile(getFrame(), ".sss");
        if(path == null) { return; }
        File f = new File(path);
        if(f.exists()){
            int result = DialogUtils.getBooleanDialog(getFrame(), "¿Desea sobreescribir?");
            if(result != 1){ return; }
        }
        saveModelIn(path);
    }

    public void saveModelAsOntology(){
        String path = FileUtils.selectNonExistingFile(getFrame(), ".owl");
        if(path == null) { return; }
        File f = new File(path);
        if(f.exists()){
            int result = DialogUtils.getBooleanDialog(getFrame(), "¿Desea sobreescribir?");
            if(result != 1){ return; }
        }
        saveModelOntologyIn(path, "RDF/XML");
    }

    private void saveModelIn(String lf) {
        lastFilePath = lf;
        File f = new File(lf);
        synchronized(model){
            try {
                FileUtils.writeBytesInFile(f, FileUtils.objectToXml(model));
                getFrame().setFileName(f.getName());
                getUndo().clear();
                getFrame().setSaved(true);
            } catch (Exception ex) {
                ex.printStackTrace();
                MainFrame.getInstance().error("Guardando archivo " + ex.toString());
            }
        }
        
    }

    private void saveModelOntologyIn(String lf, String type) {
        synchronized(engine.getModel()){
        try {
            engine.saveOntology(lf, type);
        } catch (Exception ex) {
            ex.printStackTrace();
            MainFrame.getInstance().error("Guardando ontologia " + ex.toString());
        }
        }
    }

    public void checkBurnedNodes() {
        List<String> nodes = engine.burnedNodes();
        String message = "";

        Iterator iter = nodes.iterator();
        while (iter.hasNext()) {
            message += "\n- " + iter.next();
        }

        if (message.isEmpty()) {
            message = "No existen nodos con fuego";
        } else {
            message = "Los Nodos con fuego son:" + message;
        }

        JOptionPane.showMessageDialog(frame, message, "Nodos con fuego", JOptionPane.INFORMATION_MESSAGE);
    }

    public void checkWetNodes() {
        List<String> nodes = engine.wetNodes();
        String message = "";

        Iterator iter = nodes.iterator();
        while (iter.hasNext()) {
            message += "\n- " + iter.next();
        }

        if (message.isEmpty()) {
            message = "No existen nodos con humedad";
        } else {
            message = "Los Nodos con fuego son:" + message;
        }

        JOptionPane.showMessageDialog(frame, message, "Nodos con humedad", JOptionPane.INFORMATION_MESSAGE);
    }

    public void checkFallenNodes() {
        List<String> nodes = engine.fallenNodes();
        String message = "";

        Iterator iter = nodes.iterator();
        while (iter.hasNext()) {
            message += "\n- " + iter.next();
        }

        if (message.isEmpty()) {
            message = "No existen nodos caídos";
        } else {
            message = "Los Nodos con fuego son:" + message;
        }

        JOptionPane.showMessageDialog(frame, message, "Nodos caídos", JOptionPane.INFORMATION_MESSAGE);
    }

    public void onFire() {
        if (engine.isAnyOnFire()) {
            JOptionPane.showMessageDialog(frame, "Existe incendio", "Eventos", JOptionPane.WARNING_MESSAGE);
        } else {
            JOptionPane.showMessageDialog(frame, "No existe incendio", "Eventos", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    public void onRain() {
        if (engine.isAnyWet()) {
            JOptionPane.showMessageDialog(frame, "Existe lluvia", "Eventos", JOptionPane.WARNING_MESSAGE);
        } else {
            JOptionPane.showMessageDialog(frame, "No existe lluvia", "Eventos", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    public void onQuake() {
        if (engine.isAnyQuake()) {
            JOptionPane.showMessageDialog(frame, "Existe temblor", "Eventos", JOptionPane.WARNING_MESSAGE);
        } else {
            JOptionPane.showMessageDialog(frame, "No existe temblor", "Eventos", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    public void openAboutDialog() {
        new AboutDialog(frame).setVisible(true);
    }

    public void openAdvancedQueryDialog() {
        queryDialog.setVisible(true);
    }

    public void prepareAdvancedQuery(boolean subjectCheck, boolean objectCheck, String subject, String predicate, String object, String condition, String operator, String value) {
        if (subject.isEmpty()) {
            JOptionPane.showMessageDialog(queryDialog, "El campo sujeto debe tener un valor", "Campos incompletos", JOptionPane.WARNING_MESSAGE);
            queryDialog.subjectFocus();
        } else if (object.isEmpty()) {
            JOptionPane.showMessageDialog(queryDialog, "El campo objeto debe tener un valor", "Campos incompletos", JOptionPane.WARNING_MESSAGE);
            queryDialog.objectFocus();
        } else {
            String query = engine.prepareQuery(subjectCheck, objectCheck, subject, predicate, object, condition, operator, value);
            queryDialog.setQueryValue(query);
        }
    }

    public void executeAdvancedQuery(String query, boolean subjectCheck, boolean objectCheck, String param1, String param2) {
        if (!subjectCheck) {
            param1 = "";
        }
        if (!objectCheck) {
            param2 = "";
        }
        try {
            List<List<String>> results = engine.querySPARQL(query, param1, param2);
            queryDialog.setResults(results);
        } catch (Exception e) {
            JOptionPane.showMessageDialog(queryDialog, "No existen resultados o la consulta esta mal formada", "Consulta avanzada", JOptionPane.INFORMATION_MESSAGE);
        }
    }

    public void closeOperation() {
        if (!getFrame().isSaved()){
            int result = DialogUtils.getBooleanDialog(getFrame(), "¿Desea guardar antes de salir?");
            if (result == 1){
                saveModel();
            }else if (result == 2){
                return;
            }
        }
        System.exit(0);
    }

}